#!/usr/bin/python2.5
# Copyright 2009 Google Inc.
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Geocode utility functions."""

import logging
from xml.dom import minidom
import urllib

def GetUnknown():
  """Return entries that are unknown."""
  return GeoLocation.all().filter("complete =", False).fetch(100)


def UpdateUnknown(address, lat, longt, approx):
  """Update an unknown entry."""
  item = GeoLocation.all().filter("address =", address.lower()).get()
  if not item:
    item = GeoLocation(address=address.lower())
  item.lat = lat
  item.longt = longt
  item.approx = approx
  item.put()
  memcache.set(address.lower(), "%s,%s,%s,%s" % (lat, longt, approx, True))


def NewItem(address, lat, longt, approx, complete=True):
  """Create a new geolocation."""
  address = address.lower()
  logging.info("Writing new location %s: %r %r %s %s", address, lat, longt,
               approx, complete)
  existing = GeoLocation.all().filter("address = ", address).fetch(100)
  for item in existing:
    item.delete()
  item = GeoLocation(address=address, approx=False)
  item.lat = lat
  item.longt = longt
  item.approx = approx
  item.complete = complete
  item.put()
  memcache.set(address, "%s,%s,%s,%s" % (lat, longt, approx, complete))


def GetItem(address, flush=False):
  """Fetch item from memcache or from the datastore.
     Args:
       address: the address to fetch
       flush: if true, flush from memcache

     Returns:
       GeoLocation object
  """
  address = address.lower()
  flushed = False
  cached = memcache.get(address.lower())
  if cached:
    lat, longt, approx, complete = cached.split(",")
    if complete == "False":
      # don't have an entry for this one.
      if flush:
        logging.info("flushing %s", address)
        memcache.delete(address.lower())
        flushed = True
    else:
      lat = float(lat)
      longt = float(longt)
      approx = approx == "True"
      if abs(lat) < 0.01 or abs(longt) < 0.01:
        memcache.delete(address.lower())
      else:
        return (lat, longt, approx)
  item = GeoLocation.all().filter("address =", address.lower()).get()
  if item:
    logging.debug("got item from datastore for %s %s %s %s", address,
                  item.complete, item.lat, item.longt)
  else:
    logging.debug("couldnt find item in datastore for %s", address)
  if not item or not item.complete:
    logging.debug("invalid item for %s", address)
    return None
  if flushed:
    memcache.set(address, "%s,%s,%s,%s" % (item.lat, item.longt, item.approx,
                                           True))
  return (item.lat, item.longt, item.approx)


def GetLocation(address, state="victoria", flush=False):
  """Get a location from either the datastore or geocoding."""
  address = address.lower()
  cached = GetItem(address, flush=flush)
  if cached:
    return cached
  logging.debug("couldn't find cached data for %s", address)
  # we have to geolocate
  geoaddress = ", ".join(reversed(address.split(","))) + ", " + state
  lat, longt = GoogleGeoCode(geoaddress)
  if lat is None:
    logging.info("first attempt at geocode failed for %r", address)
    if '/' in address:
      # thank you very much SA CFS
      geoaddress = address.split("/")[0] + ", " + state
    else:
      # try again, just town name
      geoaddress = address.split(",")[0] + ", " + state
    lat, longt = GoogleGeoCode(geoaddress)
    if lat is None:
      # couldn't even find town name
      if address.startswith('dse'):
        # DSE addresses in a different format
        location, geoaddress = address.split(',', 1)
        if '-' in geoaddress:
          town, road = geoaddress.split('-', 1)
          logging.info('DSE hack - trying %s %s %s', road, town, state)
          lat, longt = GoogleGeoCode('%s %s %s' % (road, town, state))
        elif '  ' in geoaddress:
          town, road = geoaddress.split('  ', 1)
          logging.info('DSE hack #2 - trying %s %s %s', road, town, state)
          lat, longt = GoogleGeoCode('%s %s %s' % (road, town, state))
        else:
          lat, longt = None, None
        if lat is None:
          approx, complete = False, False
          lat, longt = 0.0, 0.0
        else:
          approx, complete = False, True
      else:
        approx, complete = False, False
    else:
      approx, complete = True, True
  else:
    approx, complete = False, True
  logging.debug("GetLocation writing new item for %s %s %s", address, lat,
                longt)
  NewItem(address, lat, longt, approx=approx, complete=complete)
  return (lat, longt, approx)


def GoogleGeoCode(address):
  """Geocode an address in CFA format."""
  location = address.replace(" ", "+")
  key = ("ABQIAAAA-O3c-Om9OcvXMOJXreXHAxRlOb26qSyU154aZeLwOrF4C7-"
         "DphSw7JoLuXrcDjUb_7h-S1txkSCY7A")
  url = "http://maps.google.com/maps/geo?q="+location +"&output=xml&key="+key
  result = urllib.urlopen(url).read()
  try:
    dom = minidom.parseString(result)
  except:  # gah.
    logging.warning("bad geocode response for '%r' - '%r'",
                    location, result)
    return None, None
  geo_status = int(dom.getElementsByTagName("code")[0].childNodes[0].data)
  if geo_status == 200:
    coord = dom.getElementsByTagName("coordinates")[0].childNodes[0].data
    logging.debug("COORD is %r", coord)
    lat, longt, _ = coord.split(",")
    lat, longt = float(lat), float(longt)
    if abs(lat) < 0.01:
      return None, None
    return lat, longt
  else:
    return None, None

#
#class GeocodeHandler(webapp.RequestHandler):
#  """Do a geocode."""
#
#  def get(self):
#    """GET handler."""
#    address = self.request.get("address")
#    longt, lat, approx = GetLocation(address)
#    if lat is not None:
#      self.response.out.write("%.6f %.6f %s"%(lat, longt, approx))
#    memcache.delete(address.lower())
#
#
#class GeocodeUploadHandler(webapp.RequestHandler):
#  """Upload a bunch of records."""
#
#  def post(self):
#    """POST handler."""
#    body = self.request.get("data")
#    for line in body.split("\n"):
#      if not line.strip():
#        continue
#      els = [x.strip('" ') for x in line.split(';')]
#      try:
#        _, street, town, lat, longt, approx = els
#      except ValueError:
#        raise ValueError("got %r from %r"%(els, line))
#      approx = bool(approx.strip())
#      lat, longt = float(lat), float(longt)
#      if street.strip():
#        address = "%s, %s" % (town, street)
#      else:
#        address = town
#      cached = GetItem(address)
#      if cached:
#        logging.debug("already have %s: %r", address, cached)
#      if abs(lat) < 0.01 or abs(longt) < 0.01:
#        logging.debug("bogus entries for %s", address)
#        #NewItem(address.lower(), 0.0, 0.0, False, complete=False)
#        continue
#      else:
#        logging.debug("adding new entry for %s", address)
#        NewItem(address.lower(), lat, longt, approx, complete=True)
#    self.response.out.write("done")
#
#
#class GeocodeFlushHandler(webapp.RequestHandler):
#  """flush memcache."""
#
#  def get(self):
#    """GET handler."""
#    memcache.flush_all()
#
#
#class GeocodeEditHandler(webapp.RequestHandler):
#  """Edit existing/broken records."""
#
#  def get(self):
#    """GET handler."""
#    if self.request.get("address"):
#      address = self.request.get("address")
#      fix = GeoLocation.all().filter('address =',
#                                     self.request.get('address')).get()
#      fix.lat = float(self.request.get('lat'))
#      fix.longt = float(self.request.get('longt'))
#      fix.approx = bool(self.request.get('approx'))
#      fix.complete = True
#      fix.put()
#      memcache.delete(address)
#
#    missing = GeoLocation.all().filter('complete =', False).fetch(20)
#    self.response.out.write("<html><body>")
#    for i in missing:
#      self.response.out.write("<form>")
#      self.response.out.write("<b>"+ i.address + "</b><br/>")
#      self.response.out.write('<input type=hidden name=address value="%s">'%(
#          i.address))
#      self.response.out.write("lat: <input type=text name=lat>")
#      self.response.out.write("long: <input type=text name=long>")
#      self.response.out.write("approx? <input type=checkbox name=approx>")
#      self.response.out.write("<input type=submit name=submit value=Update>")
#      self.response.out.write("</form><p/>")
#
#
#def main2():
#  app = webapp.WSGIApplication([
#      ('/geocode/query', GeocodeHandler),
#      ('/geocode/upload', GeocodeUploadHandler),
#      ('/geocode/edit', GeocodeEditHandler),
#      ('/geocode/flush', GeocodeFlushHandler),
#      ], debug=True)
#  wsgiref.handlers.CGIHandler().run(app)
#

def main():
  tofix = []
  out = open('roads.csv', 'w')
  for line in open('roads-i.txt'):
    address = "%s, queensland, australia" %(line.strip())
    print address
    lat, long = GoogleGeoCode(address)
    print >> out, "%s,%f,%f" %(line.strip(), lat, long)
    
  fp = open('roads.txt')
  while True:
    road = fp.readline().strip()
    suburb = fp.readline().strip()
    if not road or not suburb:
      break
    print road, suburb
    address = "%s %s, queensland, australia" %(road, suburb)
    lat, long = GoogleGeoCode(address)
    if lat is None or long is None:
      tofix.append(address)
    else:
      print >> out, "%s %s,%f,%f" %(road, suburb, lat, long)
  print "ENTRIES NEED FIXING", "\n".join(tofix)

if __name__ == '__main__':
  main()
